home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 11: TSX-11 / Linux Cubed Series 11 - TSX-11 Vol 1.iso / sbin / fsck-min.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-11-30  |  17.3 KB  |  704 lines

  1. /*
  2.  * fsck.c - a file system consistency checker for Linux.
  3.  *
  4.  * (C) 1991, 1992 Linus Torvalds. This file may be redistributed
  5.  * as per the GNU copyleft.
  6.  */
  7.  
  8. /*
  9.  * 09.11.91  -  made the first rudimetary functions
  10.  *
  11.  * 10.11.91  -  updated, does checking, no repairs yet.
  12.  *        Sent out to the mailing-list for testing.
  13.  *
  14.  * 14.11.91  -    Testing seems to have gone well. Added some
  15.  *        correction-code, and changed some functions.
  16.  *
  17.  * 15.11.91  -  More correction code. Hopefully it notices most
  18.  *        cases now, and tries to do something about them.
  19.  *
  20.  * 16.11.91  -  More corrections (thanks to Mika Jalava). Most
  21.  *        things seem to work now. Yeah, sure.
  22.  *
  23.  *
  24.  * 19.04.92  -    Had to start over again from this old version, as a
  25.  *        kernel bug ate my enhanced fsck in february.
  26.  *
  27.  * I've had no time to add comments - hopefully the function names
  28.  * are comments enough. As with all file system checkers, this assumes
  29.  * the file system is quiescent - don't use it on a mounted device
  30.  * unless you can be sure nobody is writing to it (and remember that the
  31.  * kernel can write to it when it searches for files).
  32.  *
  33.  * Usuage: fsck [-larvsm] device
  34.  *    -l for a listing of all the filenames
  35.  *    -a for automatic repairs (not implemented)
  36.  *    -r for repairs (interactive) (not implemented)
  37.  *    -v for verbose (tells how many files)
  38.  *    -s for super-block info
  39.  *    -m for minix-like "mode not cleared" warnings
  40.  *
  41.  * The device may be a block device or a image of one, but this isn't
  42.  * enforced (but it's not much fun on a character device :-). 
  43.  */
  44.  
  45. #include <stdio.h>
  46. #include <unistd.h>
  47. #include <string.h>
  48. #include <fcntl.h>
  49. #include <ctype.h>
  50. #include <stdlib.h>
  51. #include <termios.h>
  52. #include <sys/stat.h>
  53.  
  54. #include <linux/fs.h>
  55. #include <linux/minix_fs.h>
  56.  
  57. #ifndef __GNUC__
  58. #error "needs gcc for the bitop-__asm__'s"
  59. #endif
  60.  
  61. #ifndef __linux__
  62. #define volatile
  63. #endif
  64.  
  65. #define ROOT_INO 1
  66.  
  67. #define UPPER(size,n) ((size+((n)-1))/(n))
  68. #define INODE_SIZE (sizeof(struct minix_inode))
  69. #define INODE_BLOCKS UPPER(INODES,MINIX_INODES_PER_BLOCK)
  70. #define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE)
  71.  
  72. #define BITS_PER_BLOCK (BLOCK_SIZE<<3)
  73.  
  74. static char * program_name = "fsck";
  75. static char * device_name = NULL;
  76. static int IN;
  77. static int repair=0, automatic=0, verbose=0, list=0, show=0, warn_mode=0;
  78. static int directory=0, regular=0, blockdev=0, chardev=0, links=0,
  79.         symlinks=0, total=0;
  80.  
  81. static int changed = 0; /* flags if the filesystem has been changed */
  82.  
  83. /* File-name data */
  84. #define MAX_DEPTH 50
  85. static int name_depth = 0;
  86. static char name_list[MAX_DEPTH][MINIX_NAME_LEN+1];
  87.  
  88. static char * inode_buffer = NULL;
  89. #define Inode (((struct minix_inode *) inode_buffer)-1)
  90. static char super_block_buffer[BLOCK_SIZE];
  91. #define Super (*(struct minix_super_block *)super_block_buffer)
  92. #define INODES ((unsigned long)Super.s_ninodes)
  93. #define ZONES ((unsigned long)Super.s_nzones)
  94. #define IMAPS ((unsigned long)Super.s_imap_blocks)
  95. #define ZMAPS ((unsigned long)Super.s_zmap_blocks)
  96. #define FIRSTZONE ((unsigned long)Super.s_firstdatazone)
  97. #define ZONESIZE ((unsigned long)Super.s_log_zone_size)
  98. #define MAXSIZE ((unsigned long)Super.s_max_size)
  99. #define MAGIC (Super.s_magic)
  100. #define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS)
  101.  
  102. static char inode_map[BLOCK_SIZE * MINIX_I_MAP_SLOTS];
  103. static char zone_map[BLOCK_SIZE * MINIX_Z_MAP_SLOTS];
  104.  
  105. static unsigned char * inode_count = NULL;
  106. static unsigned char * zone_count = NULL;
  107.  
  108. void recursive_check(unsigned int ino);
  109.  
  110. #define bitop(name,op) \
  111. static inline int name(char * addr,unsigned int nr) \
  112. { \
  113. int __res; \
  114. __asm__ __volatile__("bt" op "l %1,%2; adcl $0,%0" \
  115. :"=g" (__res) \
  116. :"r" (nr),"m" (*(addr)),"0" (0)); \
  117. return __res; \
  118. }
  119.  
  120. bitop(bit,"")
  121. bitop(setbit,"s")
  122. bitop(clrbit,"r")
  123.  
  124. #define inode_in_use(x) (bit(inode_map,(x)))
  125. #define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1))
  126.  
  127. #define mark_inode(x) (setbit(inode_map,(x)),changed=1)
  128. #define unmark_inode(x) (clrbit(inode_map,(x)),changed=1)
  129.  
  130. #define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1),changed=1)
  131. #define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1),changed=1)
  132.  
  133. /*
  134.  * Volatile to let gcc know that this doesn't return. When trying
  135.  * to compile this under minix, volatile gives a warning, as
  136.  * exit() isn't defined as volatile under minix.
  137.  */
  138. volatile void fatal_error(const char * fmt_string)
  139. {
  140.     fprintf(stderr,fmt_string,program_name,device_name);
  141.     exit(1);
  142. }
  143.  
  144. #define usage() fatal_error("Usage: %s [-larvsm] /dev/name\n")
  145. #define die(str) fatal_error("%s: " str "\n")
  146.  
  147. /*
  148.  * This simply goes through the file-name data and prints out the
  149.  * current file.
  150.  */
  151. void print_current_name(void)
  152. {
  153.     int i=0;
  154.  
  155.     while (i<name_depth)
  156.         printf("/%.14s",name_list[i++]);
  157. }
  158.  
  159. int ask(const char * string,int def)
  160. {
  161.     int c;
  162.  
  163.     if (!repair) {
  164.         printf("\n");
  165.         return 0;
  166.     }
  167.     if (automatic) {
  168.         printf("\n");
  169.         return def;
  170.     }
  171.     printf(def?"%s (y/n)? ":"%s (n/y)? ",string);
  172.     for (;;) {
  173.         fflush(stdout);
  174.         if ((c=getchar())==EOF)
  175.             return def;
  176.         c=toupper(c);
  177.         if (c == 'Y') {
  178.             def = 1;
  179.             break;
  180.         } else if (c == 'N') {
  181.             def = 0;
  182.             break;
  183.         } else if (c == ' ' || c == '\n')
  184.             break;
  185.     }
  186.     if (def)
  187.         printf("y\n");
  188.     else
  189.         printf("n\n");
  190.     return def;
  191. }
  192.  
  193. /*
  194.  * check_zone_nr checks to see that *nr is a valid zone nr. If it
  195.  * isn't, it will possibly be repaired. Check_zone_nr sets *corrected
  196.  * if an error was corrected, and returns the zone (0 for no zone
  197.  * or a bad zone-number).
  198.  */
  199. int check_zone_nr(unsigned short * nr, int * corrected)
  200. {
  201.     if (!*nr)
  202.         return 0;
  203.     if (*nr < FIRSTZONE)
  204.         printf("Zone nr < FIRSTZONE in file `");
  205.     else if (*nr >= ZONES)
  206.         printf("Zone nr >= ZONES in file `");
  207.     else
  208.         return *nr;
  209.     print_current_name();
  210.     printf("'.");
  211.     if (ask("Remove block",1)) {
  212.         *nr = 0;
  213.         *corrected = 1;
  214.     }
  215.     return 0;
  216. }
  217.  
  218. /*
  219.  * read-block reads block nr into the buffer at addr.
  220.  */
  221. void read_block(unsigned int nr, char * addr)
  222. {
  223.     if (!nr) {
  224.         memset(addr,0,BLOCK_SIZE);
  225.         return;
  226.     }
  227.     if (BLOCK_SIZE*nr != lseek(IN, BLOCK_SIZE*nr, SEEK_SET)) {
  228.         printf("Read error: unable to seek to block in file '");
  229.         print_current_name();
  230.         printf("'\n");
  231.         memset(addr,0,BLOCK_SIZE);
  232.     } else if (BLOCK_SIZE != read(IN, addr, BLOCK_SIZE)) {
  233.         printf("Read error: bad block in file '");
  234.         print_current_name();
  235.         printf("'\n");
  236.         memset(addr,0,BLOCK_SIZE);
  237.     }
  238. }
  239.  
  240. /*
  241.  * write_block writes block nr to disk.
  242.  */
  243. void write_block(unsigned int nr, char * addr)
  244. {
  245.     if (!nr)
  246.         return;
  247.     if (nr < FIRSTZONE || nr >= ZONES) {
  248.         printf("Internal error: trying to write bad block\n"
  249.         "Write request ignored\n");
  250.         return;
  251.     }
  252.     if (BLOCK_SIZE*nr != lseek(IN, BLOCK_SIZE*nr, SEEK_SET))
  253.         die("seek failed in write_block");
  254.     if (BLOCK_SIZE != write(IN, addr, BLOCK_SIZE)) {
  255.         printf("Write error: bad block in file '");
  256.         print_current_name();
  257.         printf("'\n");
  258.     }
  259. }
  260.  
  261. /*
  262.  * map-block calculates the absolute block nr of a block in a file.
  263.  * It sets 'changed' if the inode has needed changing, and re-writes
  264.  * any indirect blocks with errors.
  265.  */
  266. int map_block(struct minix_inode * inode, unsigned int blknr)
  267. {
  268.     unsigned short ind[BLOCK_SIZE>>1];
  269.     unsigned short dind[BLOCK_SIZE>>1];
  270.     int blk_chg, block, result;
  271.  
  272.     if (blknr<7)
  273.         return check_zone_nr(inode->i_zone + blknr, &changed);
  274.     blknr -= 7;
  275.     if (blknr<512) {
  276.         block = check_zone_nr(inode->i_zone + 7, &changed);
  277.         read_block(block, (char *) ind);
  278.         blk_chg = 0;
  279.         result = check_zone_nr(blknr + ind, &blk_chg);
  280.         if (blk_chg)
  281.             write_block(block, (char *) ind);
  282.         return result;
  283.     }
  284.     blknr -= 512;
  285.     block = check_zone_nr(inode->i_zone + 8, &changed);
  286.     read_block(block, (char *) dind);
  287.     blk_chg = 0;
  288.     result = check_zone_nr(dind + (blknr/512), &blk_chg);
  289.     if (blk_chg)
  290.         write_block(block, (char *) dind);
  291.     block = result;
  292.     read_block(block, (char *) ind);
  293.     blk_chg = 0;
  294.     result = check_zone_nr(ind + (blknr%512), &blk_chg);
  295.     if (blk_chg)
  296.         write_block(block, (char *) ind);
  297.     return result;
  298. }
  299.  
  300. void write_tables(void)
  301. {
  302.     if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET))
  303.         die("seek failed in write_tables");
  304.     if (BLOCK_SIZE != write(IN, super_block_buffer, BLOCK_SIZE))
  305.         die("unable to write super-block");
  306.     if (IMAPS*BLOCK_SIZE != write(IN,inode_map,IMAPS*BLOCK_SIZE))
  307.         die("Unable to write inode map");
  308.     if (ZMAPS*BLOCK_SIZE != write(IN,zone_map,ZMAPS*BLOCK_SIZE))
  309.         die("Unable to write zone map");
  310.     if (INODE_BUFFER_SIZE != write(IN,inode_buffer,INODE_BUFFER_SIZE))
  311.         die("Unable to write inodes");
  312. }
  313.  
  314. void read_tables(void)
  315. {
  316.     memset(inode_map,0,sizeof(inode_map));
  317.     memset(zone_map,0,sizeof(zone_map));
  318.     if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET))
  319.         die("seek failed");
  320.     if (BLOCK_SIZE != read(IN, super_block_buffer, BLOCK_SIZE))
  321.         die("unable to read super block");
  322.     if (MAGIC != MINIX_SUPER_MAGIC)
  323.         die("bad magic number in super-block");
  324.     if (ZONESIZE != 0 || BLOCK_SIZE != 1024)
  325.         die("Only 1k blocks/zones supported");
  326.     if (!IMAPS || IMAPS > MINIX_I_MAP_SLOTS)
  327.         die("bad s_imap_blocks field in super-block");
  328.     if (!ZMAPS || ZMAPS > MINIX_Z_MAP_SLOTS)
  329.         die("bad s_zmap_blocks field in super-block");
  330.     inode_buffer = malloc(INODE_BUFFER_SIZE);
  331.     if (!inode_buffer)
  332.         die("Unable to allocate buffer for inodes");
  333.     inode_count = malloc(INODES);
  334.     if (!inode_count)
  335.         die("Unable to allocate buffer for inode count");
  336.     zone_count = malloc(ZONES);
  337.     if (!zone_count)
  338.         die("Unable to allocate buffer for zone count");
  339.     if (IMAPS*BLOCK_SIZE != read(IN,inode_map,IMAPS*BLOCK_SIZE))
  340.         die("Unable to read inode map");
  341.     if (ZMAPS*BLOCK_SIZE != read(IN,zone_map,ZMAPS*BLOCK_SIZE))
  342.         die("Unable to read zone map");
  343.     if (INODE_BUFFER_SIZE != read(IN,inode_buffer,INODE_BUFFER_SIZE))
  344.         die("Unable to read inodes");
  345.     if (NORM_FIRSTZONE != FIRSTZONE)
  346.         printf("Warning: Firstzone != Norm_firstzone\n");
  347.     if (show) {
  348.         printf("%d inodes\n",INODES);
  349.         printf("%d blocks\n",ZONES);
  350.         printf("Firstdatazone=%d (%d)\n",FIRSTZONE,NORM_FIRSTZONE);
  351.         printf("Zonesize=%d\n",BLOCK_SIZE<<ZONESIZE);
  352.         printf("Maxsize=%d\n\n",MAXSIZE);
  353.     }
  354. }
  355.  
  356. struct minix_inode * get_inode(unsigned int nr)
  357. {
  358.     struct minix_inode * inode;
  359.  
  360.     if (!nr || nr >= INODES)
  361.         return NULL;
  362.     total++;
  363.     inode = Inode + nr;
  364.     if (!inode_count[nr]) {
  365.         if (!inode_in_use(nr)) {
  366.             printf("Inode %d marked not used, but used for file '",
  367.                 nr);
  368.             print_current_name();
  369.             printf("'\n");
  370.             if (repair)
  371.                 if (ask("Mark in use",1))
  372.                     mark_inode(nr);
  373.         }
  374.         if (S_ISDIR(inode->i_mode))
  375.             directory++;
  376.         else if (S_ISREG(inode->i_mode))
  377.             regular++;
  378.         else if (S_ISCHR(inode->i_mode))
  379.             chardev++;
  380.         else if (S_ISBLK(inode->i_mode))
  381.             blockdev++;
  382.         else if (S_ISLNK(inode->i_mode))
  383.             symlinks++;
  384.     } else
  385.         links++;
  386.     if (!++inode_count[nr]) {
  387.         printf("Warning: inode count too big.\n");
  388.         inode_count[nr]--;
  389.     }
  390.     return inode;
  391. }
  392.  
  393. void check_root(void)
  394. {
  395.     struct minix_inode * inode = Inode + ROOT_INO;
  396.  
  397.     if (!inode || !S_ISDIR(inode->i_mode))
  398.         die("root inode isn't a directory");
  399. }
  400.  
  401. static int add_zone(unsigned short * znr, int * corrected)
  402. {
  403.     int result;
  404.     int block;
  405.  
  406.     result = 0;
  407.     block = check_zone_nr(znr, corrected);
  408.     if (!block)
  409.         return 0;
  410.     if (zone_count[block]) {
  411.         printf("Block has been used before. Now in file `");
  412.         print_current_name();
  413.         printf("'.");
  414.         if (ask("Clear",1)) {
  415.             *znr = 0;
  416.             block = 0;
  417.             *corrected = 1;
  418.         }
  419.     }
  420.     if (!block)
  421.         return 0;
  422.     if (!zone_in_use(block)) {
  423.         printf("Block %d in file `",block);
  424.         print_current_name();
  425.         printf("' is marked not in use.");
  426.         if (ask("Correct",1))
  427.             mark_zone(block);
  428.     }
  429.     if (!++zone_count[block])
  430.         zone_count[block]--;
  431.     return block;
  432. }
  433.  
  434. static void add_zone_ind(unsigned short * znr, int * corrected)
  435. {
  436.     static char blk[BLOCK_SIZE];
  437.     int i, chg_blk=0;
  438.     int block;
  439.  
  440.     block = add_zone(znr, corrected);
  441.     if (!block)
  442.         return;
  443.     read_block(block, blk);
  444.     for (i=0 ; i < (BLOCK_SIZE>>1) ; i++)
  445.         add_zone(i + (unsigned short *) blk, &chg_blk);
  446.     if (chg_blk)
  447.         write_block(block, blk);
  448. }
  449.  
  450. static void add_zone_dind(unsigned short * znr, int * corrected)
  451. {
  452.     static char blk[BLOCK_SIZE];
  453.     int i, blk_chg=0;
  454.     int block;
  455.  
  456.     block = add_zone(znr, corrected);
  457.     if (!block)
  458.         return;
  459.     read_block(block, blk);
  460.     for (i=0 ; i < (BLOCK_SIZE>>1) ; i++)
  461.         add_zone_ind(i + (unsigned short *) blk, &blk_chg);
  462.     if (blk_chg)
  463.         write_block(block, blk);
  464. }
  465.  
  466. void check_zones(unsigned int i)
  467. {
  468.     struct minix_inode * inode;
  469.  
  470.     if (!i || i >= INODES)
  471.         return;
  472.     if (inode_count[i] > 1)    /* have we counted this file already? */
  473.         return;
  474.     inode = Inode + i;
  475.     if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) &&
  476.         !S_ISLNK(inode->i_mode))
  477.         return;
  478.     for (i=0 ; i<7 ; i++)
  479.         add_zone(i + inode->i_zone, &changed);
  480.     add_zone_ind(7 + inode->i_zone, &changed);
  481.     add_zone_dind(8 + inode->i_zone, &changed);
  482. }
  483.  
  484. void check_file(struct minix_inode * dir, unsigned int offset)
  485. {
  486.     static char blk[BLOCK_SIZE];
  487.     struct minix_inode * inode;
  488.     int ino;
  489.     char * name;
  490.     int block;
  491.  
  492.     block = map_block(dir,offset/BLOCK_SIZE);
  493.     read_block(block, blk);
  494.     name = blk + (offset % BLOCK_SIZE) + 2;
  495.     ino = * (unsigned short *) (name-2);
  496.     if (ino >= INODES) {
  497.         print_current_name();
  498.         printf(" contains a bad inode number for file '");
  499.         printf("%.14s'.",name);
  500.         if (ask(" Remove",1)) {
  501.             *(unsigned short *)(name-2) = 0;
  502.             write_block(block, blk);
  503.         }
  504.         ino = 0;
  505.     }    
  506.     inode = get_inode(ino);
  507.     if (!offset)
  508.         if (!inode || strcmp(".",name)) {
  509.             print_current_name();
  510.             printf(": bad directory: '.' isn't first\n");
  511.         } else return;
  512.     if (offset == 16)
  513.         if (!inode || strcmp("..",name)) {
  514.             print_current_name();
  515.             printf(": bad directory: '..' isn't second\n");
  516.         } else return;
  517.     if (!inode)
  518.         return;
  519.     if (name_depth < MAX_DEPTH)
  520.         strncpy(name_list[name_depth],name,14);
  521.     name_depth++;    
  522.     if (list) {
  523.         if (verbose)
  524.             printf("%6d %07o %3d ",ino,inode->i_mode,inode->i_nlinks);
  525.         print_current_name();
  526.         if (S_ISDIR(inode->i_mode))
  527.             printf(":\n");
  528.         else
  529.             printf("\n");
  530.     }
  531.     check_zones(ino);
  532.     if (inode && S_ISDIR(inode->i_mode))
  533.         recursive_check(ino);
  534.     name_depth--;
  535.     return;
  536. }
  537.  
  538. void recursive_check(unsigned int ino)
  539. {
  540.     struct minix_inode * dir;
  541.     unsigned int offset;
  542.  
  543.     dir = Inode + ino;
  544.     if (!S_ISDIR(dir->i_mode))
  545.         die("internal error");
  546.     if (dir->i_size < 32) {
  547.         print_current_name();
  548.         printf(": bad directory: size<32");
  549.     }
  550.     for (offset = 0 ; offset < dir->i_size ; offset += 16)
  551.         check_file(dir,offset);
  552. }
  553.  
  554. int bad_zone(int i)
  555. {
  556.     char buffer[1024];
  557.  
  558.     if (BLOCK_SIZE*i != lseek(IN, BLOCK_SIZE*i, SEEK_SET))
  559.         die("seek failed in bad_zone");
  560.     return (BLOCK_SIZE != read(IN, buffer, BLOCK_SIZE));
  561. }
  562.  
  563. void check_counts(void)
  564. {
  565.     int i;
  566.  
  567.     for (i=1 ; i < INODES ; i++) {
  568.         if (!inode_in_use(i) && Inode[i].i_mode && warn_mode) {
  569.             printf("Inode %d mode not cleared.",i);
  570.             if (ask("Clear",1)) {
  571.                 Inode[i].i_mode = 0;
  572.                 changed = 1;
  573.             }
  574.         }
  575.         if (!inode_count[i]) {
  576.             if (!inode_in_use(i))
  577.                 continue;
  578.             printf("Inode %d not used, marked used in the bitmap.",i);
  579.             if (ask("Clear",1))
  580.                 unmark_inode(i);
  581.             continue;
  582.         }
  583.         if (!inode_in_use(i)) {
  584.             printf("Inode %d used, marked unused in the bitmap.",
  585.                 i);
  586.             if (ask("Set",1))
  587.                 mark_inode(i);
  588.         }
  589.         if (Inode[i].i_nlinks != inode_count[i]) {
  590.             printf("Inode %d (mode = %07o), i_nlinks=%d, counted=%d.",
  591.                 i,Inode[i].i_mode,Inode[i].i_nlinks,inode_count[i]);
  592.             if (ask("Set i_nlinks to count",1)) {
  593.                 Inode[i].i_nlinks=inode_count[i];
  594.                 changed=1;
  595.             }
  596.         }
  597.     }
  598.     for (i=FIRSTZONE ; i < ZONES ; i++) {
  599.         if (zone_in_use(i) == zone_count[i])
  600.             continue;
  601.         if (!zone_count[i]) {
  602.             if (bad_zone(i))
  603.                 continue;
  604.             printf("Zone %d: marked in use, no file uses it.",i);
  605.             if (ask("Unmark",1))
  606.                 unmark_zone(i);
  607.             continue;
  608.         }
  609.         printf("Zone %d: %sin use, counted=%d\n",
  610.         i,zone_in_use(i)?"":"not ",zone_count[i]);
  611.     }
  612. }
  613.  
  614. void check(void)
  615. {
  616.     memset(inode_count,0,INODES*sizeof(*inode_count));
  617.     memset(zone_count,0,ZONES*sizeof(*zone_count));
  618.     check_zones(ROOT_INO);
  619.     recursive_check(ROOT_INO);
  620.     check_counts();
  621. }
  622.  
  623. int main(int argc, char ** argv)
  624. {
  625.     struct termios termios,tmp;
  626.     int count;
  627.  
  628.     if (argc && *argv)
  629.         program_name = *argv;
  630.     if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE)
  631.         die("bad inode size");
  632.     while (argc-- > 1) {
  633.         argv++;
  634.         if (argv[0][0] != '-')
  635.             if (device_name)
  636.                 usage();
  637.             else
  638.                 device_name = argv[0];
  639.         else while (*++argv[0])
  640.             switch (argv[0][0]) {
  641.                 case 'l': list=1; break;
  642.                 case 'a': automatic=1; repair=1; break;
  643.                 case 'r': automatic=0; repair=1; break;
  644.                 case 'v': verbose=1; break;
  645.                 case 's': show=1; break;
  646.                 case 'm': warn_mode=1; break;
  647.                 default: usage();
  648.             }
  649.     }
  650.     if (!device_name)
  651.         usage();
  652.     if (repair && !automatic) {
  653.         if (!isatty(0) || !isatty(1))
  654.             die("need terminal for interactive repairs");
  655.         tcgetattr(0,&termios);
  656.         tmp = termios;
  657.         tmp.c_lflag &= ~(ICANON|ECHO);
  658.         tcsetattr(0,TCSANOW,&tmp);
  659.     }
  660.     IN = open(device_name,repair?O_RDWR:O_RDONLY);
  661.     if (IN < 0)
  662.         die("unable to open '%s'");
  663.     for (count=0 ; count<3 ; count++)
  664.         sync();
  665.     read_tables();
  666.     check_root();
  667.     check();
  668.     if (verbose) {
  669.         int i, free;
  670.  
  671.         for (i=1,free=0 ; i < INODES ; i++)
  672.             if (!inode_in_use(i))
  673.                 free++;
  674.         printf("\n%6d inodes used (%d%%)\n",(INODES-free-1),
  675.             100*(INODES-free-1)/(INODES-1));
  676.         for (i=FIRSTZONE,free=0 ; i < ZONES ; i++)
  677.             if (!zone_in_use(i))
  678.                 free++;
  679.         printf("%6d zones used (%d%%)\n",(ZONES-free),
  680.             100*(ZONES-free)/ZONES);
  681.         printf("\n%6d regular files\n"
  682.         "%6d directories\n"
  683.         "%6d character device files\n"
  684.         "%6d block device files\n"
  685.         "%6d links\n"
  686.         "%6d symbolic links\n"
  687.         "------\n"
  688.         "%6d files\n",
  689.         regular,directory,chardev,blockdev,
  690.         links-2*directory+1,symlinks,total-2*directory+1);
  691.     }
  692.     if (changed) {
  693.         write_tables();
  694.         printf(    "----------------------------\n"
  695.             "FILE SYSTEM HAS BEEN CHANGED\n"
  696.             "----------------------------\n");
  697.         for (count=0 ; count<3 ; count++)
  698.             sync();
  699.     }
  700.     if (repair && !automatic)
  701.         tcsetattr(0,TCSANOW,&termios);
  702.     return (0);
  703. }
  704.